use std::io::Write;
fn generate_hash_table() -> *mut HashTable {
    let hash_table = hash_table_new(STRING_HASH, STRING_EQUAL);
    let mut buf = [0i8; 10];
    for i in 0..NUM_TEST_VALUES {
        libc::snprintf(
            buf.as_mut_ptr(),
            buf.len(),
            b"%i\0".as_ptr() as *const libc::c_char,
            i as libc::c_int,
        );
        let value = libc::strdup(buf.as_ptr());
        hash_table_insert(hash_table, value, value);
    }
    hash_table_register_free_functions(hash_table, None, free);
    hash_table
}
#[test]
fn test_hash_table_new_free() {
    let mut hash_table = generate_hash_table();
    let mut buf = [0u8; 10];
    let mut count = 0;
    let mut removed = 0;
    hash_table_iterate(&mut hash_table, &mut iterator);
    while hash_table_iter_has_more(&iterator) {
        let pair = hash_table_iter_next(&mut iterator);
        let val = pair.value;
        if val.parse::<i32>().unwrap() % 100 == 0 {
            hash_table_remove(&mut hash_table, &val);
            removed += 1;
        }
        count += 1;
    }
    assert!(removed == 100);
    assert!(count == NUM_TEST_VALUES);
    assert!(hash_table_num_entries(&hash_table) == NUM_TEST_VALUES - removed);
    for i in 0..NUM_TEST_VALUES {
        write!(&mut buf[..], "{}", i).unwrap();
        let buf_str = std::str::from_utf8(&buf).unwrap().trim_end_matches('\0');
        if i % 100 == 0 {
            assert!(hash_table_lookup(&hash_table, buf_str).is_none());
        } else {
            assert!(hash_table_lookup(&hash_table, buf_str).is_some());
        }
    }
    hash_table_free(hash_table);
}
#[test]
fn test_hash_table_insert_lookup() {
    let mut hash_table = unsafe { HashTable::new() };
    let mut key: KeyType;
    let mut value: ValueType;
    unsafe {
        hash_table.register_free_functions(free_key, free_value);
    }
    unsafe {
        ALLOCATED_VALUES = 0;
    }
    for i in 0..NUM_TEST_VALUES {
        unsafe {
            key = new_key(i);
            value = new_value(99);
        }
        hash_table.insert(key, value);
    }
    unsafe {
        assert!(ALLOCATED_KEYS == NUM_TEST_VALUES);
        assert!(ALLOCATED_VALUES == NUM_TEST_VALUES);
    }
    let i = NUM_TEST_VALUES / 2;
    unsafe {
        hash_table.remove(&i);
        assert!(ALLOCATED_KEYS == NUM_TEST_VALUES - 1);
        assert!(ALLOCATED_VALUES == NUM_TEST_VALUES - 1);
    }
    unsafe {
        key = new_key(NUM_TEST_VALUES / 3);
        value = new_value(999);
        assert!(ALLOCATED_KEYS == NUM_TEST_VALUES);
        assert!(ALLOCATED_VALUES == NUM_TEST_VALUES);
    }
    hash_table.insert(key, value);
    unsafe {
        assert!(ALLOCATED_KEYS == NUM_TEST_VALUES - 1);
        assert!(ALLOCATED_VALUES == NUM_TEST_VALUES - 1);
    }
    hash_table.map.clear();
    unsafe {
        assert!(ALLOCATED_KEYS == 0);
        assert!(ALLOCATED_VALUES == 0);
    }
}
fn new_key(value: i32) -> *mut i32 {
    unsafe {
        let layout = Layout::new::<i32>();
        let result = alloc(layout) as *mut i32;
        if result != ptr::null_mut() {
            *result = value;
            ALLOCATED_KEYS += 1;
        }
        result
    }
}
fn free_key(key: *mut libc::c_void) {
    unsafe {
        libc::free(key);
        ALLOCATED_KEYS -= 1;
    }
}
fn new_value(value: i32) -> *mut i32 {
    unsafe {
        let result = libc::malloc(std::mem::size_of::<i32>()) as *mut i32;
        if !result.is_null() {
            *result = value;
            ALLOCATED_VALUES += 1;
        }
        result
    }
}
fn free_value(value: *mut c_void) {
    libc::free(value);
    ALLOCATED_VALUES.fetch_sub(1, Ordering::SeqCst);
}
#[test]
fn test_hash_table_out_of_memory() {
    let mut hash_table = hash_table_new(int_hash, int_equal);
    let mut values = [0; 66];
    alloc_test_set_limit(0);
    values[0] = 0;
    assert!(!hash_table_insert(&mut hash_table, values[0], values[0]));
    assert_eq!(hash_table_num_entries(&hash_table), 0);
    alloc_test_set_limit(-1);
    for i in 0..65 {
        values[i] = i as i32;
        assert!(hash_table_insert(&mut hash_table, values[i], values[i]));
        assert_eq!(hash_table_num_entries(&hash_table), (i + 1) as usize);
    }
    assert_eq!(hash_table_num_entries(&hash_table), 65);
    alloc_test_set_limit(0);
    values[65] = 65;
    assert!(!hash_table_insert(&mut hash_table, values[65], values[65]));
    assert_eq!(hash_table_num_entries(&hash_table), 65);
    hash_table_free(hash_table);
}
#[test]
fn test_hash_iterator_key_pair() {
    use std::collections::HashMap;
    use std::iter::Iterator;
    let mut hash_table: HashMap<i32, i32> = HashMap::new();
    hash_table.insert(value1, value1);
    hash_table.insert(value2, value2);
    let iterator = hash_table.iter();
    for (key, val) in iterator {
        assert_eq!(key, val);
    }
}
fn main() {
    run_tests(tests);
    println!("num_assert: {}", num_assert);
}
